<?php
/**
 * hash class file
 *
 * 一致性hash类
 * @author DaBing<InitPHP>, tommy
 * @copyright  CopyRight DoitPHP team, initphp team
 * @link http://www.doitphp.com
 * @license New BSD License.{@link http://www.opensource.org/licenses/bsd-license.php}
 * @version $Id: hash.class.php 1.3 2011-11-13 21:03:01Z tommy $
 * @package libraries
 * @since 1.0
 */

if (!defined('IN_DOIT')) {
    exit();
}

class hash extends Base {

    /**
     * 存放hash对应值
     *
     * @var array
     */
    private static $hash_table = array();

    /**
     * 存放值
     * @var array
     */
    private static $hash_list = array();

    /**
     * hash_table初始化
     *
     * false为关闭;true开启，当初始化完毕之后，关闭该功能
     * @var boolean
     */
    private $is_init;

    /**
     * hash_table缓存路劲
     *
     * @var string
     */
    private $filename;

    /**
     * 构造函数
     *
     * @access public
     * @return boolean
     */
    public function __construct() {

        $this->filename  = CACHE_DIR . 'data/doitphp_hash.cache.php';
        $this->is_init   = is_file($this->filename) ? false : true;

        return true;
    }

    /**
     *    一致性hash：添加节点
     *
     *  只有当且仅当开启is_init初始化hash_table的时候, hash_table文件才会生成
     *  @access pulic
     *  @param  string  $node    字符串(也可以是数组)
     *  @param  string  $num     虚拟节点
     *  @return integer
     */
    public function add_node($node, $num = 0) {

        //parse params
        if (!$node) {
            return false;
        }

        //当初始化状态开启时...创建缓存文件
        if ($this->is_init === true) {
            if (is_array($node)) {
                foreach ($node as $string) {
                    $key = $this->hash_md5($string);
                    self::$hash_table[$this->hash_crc($key)] = array($key, $string);
                    $this->add_virtual_node($string, $num);
                    self::$hash_list[] = $this->hash_crc($key);
                }
                sort(self::$hash_list);
            }

            //创建hash缓存文件
            $hash_cache_array = array(
            'hash_table'    => self::$hash_table,
            'hash_list'     => self::$hash_list,
            );

            $hash_cache_content = "<?php\r\nif (!defined('IN_DOIT')) exit();\r\nreturn " . var_export($hash_cache_array, true) . ";";
            file_put_contents($this->filename, $hash_cache_content, LOCK_EX);
        }

        return true;
    }

    /**
     *    一致性hash：获取hash对应的节点值
     *
     *  @param  string  $string    字符串
     *  @return int
     */
    public function get_node($string) {

        //parse params
        if (!$string) {
            return false;
        }

        $key         = $this->hash_md5($string);
        $key_val     = $this->hash_crc($key);
        $result      = $start = 0;

        //当hash table等数据为空时
        if (!self::$hash_table && !self::$hash_list) {
            $hash_cache_data  = include $this->filename;
            self::$hash_table = $hash_cache_data['hash_table'];
            self::$hash_list  = $hash_cache_data['hash_list'];
        }

        foreach (self::$hash_list as $value) {
            if ($start == 0) {
                $result = $value;
            }
            if ($key_val < $value) {
                $result = $value;
                break;
            }
            $start = 1;
        }

        return self::$hash_table[$result][1];
    }

    /**
     *    一致性hash：生成虚拟节点
     *
     *  @param  string  $string  字符串
     *  @param  string  $num     虚拟节点
     *  @return boolean
     */
    public function add_virtual_node($string, $num) {

        //parse params
        if (!$string) {
            return false;
        }
        $num = (int) $num;

        if ($num < 1) {
            return false;
        }

        for ($i = 0; $i < $num; $i ++) {
            $key = $this->hash_md5($string . '#' . $i);
            self::$hash_table[$this->hash_crc($key)] = array($key, $string);
            self::$hash_list[] = $this->hash_crc($key);
        }

        return true;
    }

    /**
     *    一致性hash，计算一个字符串的 crc32 多项式
     *
     *  @param  string  $string  字符串
     *  @return int
     */
    private function hash_crc($string) {

        return crc32($string);
    }

    /**
     *    一致性hash：MD5加密得到KEY值
     *
     *  @param  string  $string  字符串
     *  @return int
     */
    private function hash_md5($string) {

        return md5($string);
    }
}